package cmd

import (
	"bufio"
	"fmt"
	"github.com/spf13/ktl/kube"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

var clientSet *kubernetes.Clientset

func SetClient(client *kubernetes.Clientset) {
	clientSet = client
}

func init() {
	rootCmd.Flags().BoolP("use-context", "c", false, "kubectl config use-context <context>")
	rootCmd.Flags().BoolP("namespace", "n", false, "kubectl config namespace <namespace>")
	rootCmd.Flags().BoolP("attach", "a", true, "kubectl attach -ti <podName> /bin/bash")
	rootCmd.Flags().BoolP("exec", "e", false, "kubectl exec -ti <podName> /bin/sh")
	rootCmd.Flags().BoolP("logs", "l", false, "kubectl logs -f <podName>")
	rootCmd.Flags().BoolP("describe", "d", false, "kubectl describe pod/<podName>")
}

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:     "ktl",
	Version: "1.0.0",
	Short:   "A tool to quickly find kubernetes pod container information and container log information",
	Long: `A tool to quickly find kubernetes pod container information and container log information,
Applications usually use the ~/.kube/config configuration file as the default, 
but if your configuration file is not in this directory, you can configure the 
environment variable KTL_CONFIG_PATH to specify the configuration file path.`,
	Example: `
ktl dm  		Find a list of kubernetes containers whose Pod name contains a 'dm' string
ktl dm-data -l 		Looking for kubernetes logs for containers with a Pod name containing a 'dm' string will 
			bring you to the selection screen if there are multiple identical containers
ktl -c  		List current context and all contexts
ktl prod -c 		Change the current context to prod`,

	//Uncomment the following line if your bare application
	//has an action associated with it:
	Run: func(cmd *cobra.Command, args []string) {
		useContext, _ := cmd.Flags().GetBool("use-context")
		logs, _ := cmd.Flags().GetBool("logs")
		attach, _ := cmd.Flags().GetBool("attach")
		exec, _ := cmd.Flags().GetBool("exec")
		describe, _ := cmd.Flags().GetBool("describe")
		namespace, _ := cmd.Flags().GetBool("namespace")
		//useContext flag
		if useContext {
			cmdUseContextFlag(cmd, args)
			return
		}
		if namespace {
			cmdNamespaceFlag(args)
			return
		}
		//connectK8s and search pod
		pods := searchPodName(args)
		if len(pods) == 0 {
			fmt.Println("Not found  any pod,program break")
			return
		}
		//logs flag
		if logs {
			cmdLogsFlag(pods)
			return
		}
		//exec
		if exec {
			cmdExecFlag(pods)
			return
		}
		//describe
		if describe {
			cmdDescribeFlag(pods)
			return
		}
		//attach flag 默认处理方式
		if attach {
			cmdAttachFlag(pods)
		}
	},
}

func cmdNamespaceFlag(args []string) {
	kubeConfig := kube.KubeConfig()
	if args == nil || len(args) == 0 {
		fmt.Printf("current context: %s,current namespace: %s\n", kubeConfig.CurrentContext, kubeConfig.Contexts[kubeConfig.CurrentContext].Namespace)
	} else {
		kubeConfig.Contexts[kubeConfig.CurrentContext].Namespace = args[0]
		kube.WriteConfig(*kubeConfig, kube.ConfigPath())
		fmt.Printf("current context: %s,current namespace: %s\n", kubeConfig.CurrentContext, kubeConfig.Contexts[kubeConfig.CurrentContext].Namespace)
	}
}

func cmdDescribeFlag(pods []corev1.Pod) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Printf("input Pod No [1-%d],example [1]:  ", len(pods))
	input, _ := reader.ReadString('\n')
	input = strings.Replace(strings.Replace(input, "\r\n", "", -1), "\n", "", -1)
	inputNo, err := strconv.Atoi(input)
	if err != nil {
		fmt.Println("Pod No input error,program break;")
		return
	}
	if inputNo > 0 && inputNo <= len(pods) {
		kube.DescribePod(clientSet, pods[inputNo-1])
	} else {
		fmt.Println("Pod No input error,program break;")
	}
}

func cmdLogsFlag(pods []corev1.Pod) {
	var tailLines = int64(100)
	if len(pods) == 1 {
		kube.PrintLogPodByFollow(clientSet, pods[0].Name, &tailLines)
	} else {
		selectorPodPrintLogByFollow(pods, tailLines)
	}
}

func cmdAttachFlag(pods []corev1.Pod) {
	if len(pods) == 1 {
		kube.ExecCommandPod(clientSet, pods[0], "/bin/bash")
	} else {
		selectorPodExec(pods, "/bin/bash")
	}
}

func cmdExecFlag(pods []corev1.Pod) {
	if len(pods) == 1 {
		kube.ExecCommandPod(clientSet, pods[0], "/bin/sh")
	} else {
		selectorPodExec(pods, "/bin/sh")
	}
}

func searchPodName(args []string) []corev1.Pod {
	//需要连接K8s
	if clientSet == nil {
		connect, _ := kube.ConnectByConfig(kube.KubeConfig())
		SetClient(connect)
	}
	var pods []corev1.Pod
	if args == nil || len(args) == 0 {
		pods = kube.PrintPod(clientSet, "")
	} else {
		pods = kube.PrintPod(clientSet, args[0])
	}
	return pods
}

func cmdUseContextFlag(cmd *cobra.Command, args []string) {
	if args == nil || len(args) == 0 {
		kubeConfig := kube.KubeConfig()
		fmt.Printf("Kube current-context:  %s\n", kubeConfig.CurrentContext)
		// 获取所有的键
		keys := make([]string, 0, len(kubeConfig.Contexts))
		for k := range kubeConfig.Contexts {
			keys = append(keys, k)
		}
		// 使用strings.Join将键以逗号连接
		contextNames := strings.Join(keys, ", ")
		fmt.Printf("kube contexts:  %s\n", contextNames)
	} else {
		name := args[0]
		config := kube.KubeConfig()
		if config.Contexts[name] != nil {
			if config.CurrentContext != name {
				config.CurrentContext = name
				kube.WriteConfig(*config, kube.ConfigPath())
				fmt.Printf("kube current-context:  %s\n", name)
			} else {
				fmt.Printf("kube current-context:  %s\n", name)
			}
		} else {
			fmt.Printf("Not found kube context:  %s\n", name)
			os.Exit(0)
		}
	}
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func selectorPodExec(pods []corev1.Pod, command string) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Printf("input Pod No [1-%d],example [1]:  ", len(pods))
	input, _ := reader.ReadString('\n')
	input = strings.Replace(strings.Replace(input, "\r\n", "", -1), "\n", "", -1)
	inputNo, err := strconv.Atoi(input)
	if err != nil {
		fmt.Println("Pod No input error,program break;")
		return
	}
	if inputNo > 0 && inputNo <= len(pods) {
		kube.ExecCommandPod(clientSet, pods[inputNo-1], command)
	} else {
		fmt.Println("Pod No input error,program break;")
	}
}

// selectorPodPrintLogByFollow 选择POD并打印日志
func selectorPodPrintLogByFollow(pods []corev1.Pod, tailLines int64) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Printf("input Pod No [1-%d]/[tail lines],example [1/1000]:  ", len(pods))
	input, _ := reader.ReadString('\n')
	input = strings.Replace(strings.Replace(input, "\r\n", "", -1), "\n", "", -1)
	inputs := strings.Split(input, "/")
	if len(inputs) == 0 || len(inputs) > 2 {
		fmt.Println("Pod No input error,program break;")
		return
	}
	inputNo, err := strconv.Atoi(inputs[0])
	if len(inputs) == 2 {
		t, err := strconv.Atoi(inputs[1])
		if err != nil || t > 1000 || t < 0 {
			fmt.Println("Ignore input tail lines error")
			return
		} else {
			tailLines = int64(t)
		}
	}
	if err != nil {
		fmt.Println("Pod No input error,program break;")
		return
	}
	if inputNo > 0 && inputNo <= len(pods) {
		fmt.Printf("Pod tail lines %d\n", tailLines)
		kube.PrintLogPodByFollow(clientSet, pods[inputNo-1].Name, &tailLines)
	} else {
		fmt.Println("Pod No input error,program break;")
	}
}
